/*------------------------------------------------------------------------------
$module:      files
$package:     CseApi
$purpose:     register, binary file and filename/path process
$author:      WAYNE WORK STDIO
$CreateAt:    10/05/21
$LastModify:
$license:     Mozilla Public License Version 1.1
------------------------------------------------------------------------------*/

#include <sys/types.h>
#include <sys/stat.h> 
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <errno.h>

#include "CseApi/CseApi.h"
#include "../files.h"

#define CSE_HOME_DIR_NAME          _T("CSE_HOME")

TCharArray GetCurrExeFileName()
{   // warning: only run under linux, and it requires a /proc filesystem
    TXchar path[260];
    readlink(_T("/proc/self/exe"),path,sizeof(path));
    path[259] = _T('\0');

    return path;
}

TCharArray ChangeFileExt(const TCharArray& sFile, const TCharArray& ext)
{
    TCharArray ret = sFile;

    for (int i = ret.len()-1; i >= 0; i--)
    {
        TXchar ch = ret[i];
        if (ch == _T('.'))
        {
            ret.truncate(i);
            break;
        }
        else if (ch == _T('\\') || ch == _T('/') || ch == _T(':'))
            break;
    }
    ret += ext;
    return ret;
}

TCharArray ExtractFileExt(const TCharArray& FileName)
{
    TCharArray ret;
    for (int i = FileName.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('.'))
            return ret;
        else if (ch == _T('\\') || ch == _T('/'))
            break;
        else ret = ch + ret;
    }

    ret.clear();
    return ret;
}

TCharArray ExtractFileName(const TCharArray& FileName)
{
    TCharArray ret;
    for (int i = FileName.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('\\') || ch == _T('/'))
            break;
        else ret = ch + ret;
    }
    return TCharArray(CUT_FROM,ret);
}

TCharArray ExtractFileDir(const TCharArray& FileName)
{
    TCharArray ret = FileName;
    for (int i = ret.len()-1; i >= 0; i--)
    {
        TXchar ch = FileName[i];
        if (ch == _T('\\') || ch == _T('/'))
        {
            ret.truncate(i);
            return TCharArray(CUT_FROM,ret);
        }
    }
    
    // no "/" or "\" found
    return CseEmptyStr;
}

TBool FileExists(const TXchar *FileName)
{
    struct stat stFileInfo;

    // attempt to get the file attributes
    if (0 == stat(FileName,&stFileInfo) && !S_ISDIR(stFileInfo.st_mode))
        return true;
    else return false;
}

TBool DirExists(const TXchar *DirName)
{
    struct stat stFileInfo;
    // attempt to get the file attributes
    if (0 == stat(DirName,&stFileInfo) && S_ISDIR(stFileInfo.st_mode))
        return true;
    else return false;
}

TCharArray NormalizeFileName(const TCharArray& sFile)
{
    TXchar path[260];

    if (NULL != realpath(sFile,path))
        return path;
    else return sFile;
}

TBool SearchSubDirs(TCharArray& sOut, const TCharArray& dir)
{
    ASSERT_TRUE(! dir.isEmpty());
    sOut.truncate(0);

    TCharArray TargPath = dir;
    TXchar ch = TargPath[-1];
    if (ch != _T('\\') && ch != _T('/'))
        TargPath += _T("/");

    DIR *dirp = opendir(TargPath);
    if (dirp == NULL) return false;

    struct dirent *dp;
    while ((dp = readdir(dirp)) != NULL)
    {
        TXchar filename[260];
        strcpy(filename,TargPath);
        strcat(filename,dp->d_name);

        struct stat FileStat;
        if (stat(filename,&FileStat) == 0 && S_ISDIR(FileStat.st_mode))
        {
            if (STRCMP(dp->d_name,_T(".")) != 0 && STRCMP(dp->d_name,_T("..")) != 0)
                sOut = sOut + filename + _T('\0');
        }
    }
    closedir(dirp);

    return true;
}

// Following define TDllFile
//==============================================================================
TDllFile::TDllFile(const TXchar *file)
{
    m_handle = loadLib(file);
}

TDllFile::~TDllFile()
{
    unloadLib(m_handle);
}

void *TDllFile::loadLib(const TXchar *file)
{
    void *handle = dlopen(file,RTLD_LAZY);
    CHECK_MSG(NULL != handle, _T("Load dll file error:") + TCharArray(file));
    return handle;
}

void TDllFile::unloadLib(void *handle)
{
    if (NULL != handle)
        dlclose(handle);
}

TBool TDllFile::isLoaded()
{
    return NULL != m_handle;
}

void TDllFile::attach(void * handle)
{
    ASSERT_TRUE(m_handle == NULL);
    m_handle = handle;
}

void *TDllFile::detach()
{
    void *ret = m_handle;
    m_handle = NULL;
    return ret;
}

void *TDllFile::getSymbol(const TXchar *name)
{
    CHECK_TRUE(m_handle != NULL);
    void *sym = dlsym(m_handle,name);
    const TXchar *SymError = dlerror();

    if (SymError != NULL)
        throw LOG_CSE_ERR(CSE_IO_ERR,SymError);
    return sym;
}

TCharArray Cse_GetHomeDir()
{
#if defined(__GNUC__)
    TXchar *pEnv = getenv(CSE_HOME_DIR_NAME);
    if (pEnv != NULL) return pEnv;
#endif

    return CseEmptyStr;
}
